<!--*************************************
* @date:   2021-04-27   LiLinBin  [创建文件]
* @update: 2021-04-27   LiLinBin  [编写功能]
* @update: 2021-06-28   LiLinBin  [读取每个组件提供的width,height字段,将当前组件curComp改为对象信息]
* 
* @description: 项目调试页面
****************************************-->
 
<template>
	<div class='comp-search'>

		<div class="content-box"
		    :style="contentStyle">
			<div class="content-tip"
			    v-show="showTip">请在查询框中检索你要查询的组件......
			</div>

			<div class="comp-box"
			    v-if="multipleComp.length===0 && !showTip"
			    :style="`width:${curComp.width || '200px'};height:${curComp.height || '200px'}`">
				<component :is="curComp.componentName || ''"
				    :attrConfig="layerData.attrConfig"
				    :dataConfig="layerData.dataConfig"
				    :eventConfig="layerData.eventConfig"
				    ref="curComp"></component>
			</div>

			<div class="comp-box"
			    v-for="comp in multipleComp"
			    :key="comp"
			    :style="`width:${comp.width || '200px'};height:${comp.height || '200px'};margin-top:6px;margin-right:6px`">
				<component :is="comp.componentName || ''"></component>
			</div>
		</div>

		<div class="list-box">
			<div class="li-search">
				<a-input-search class="search-item"
				    v-model:value="searchValue"
				    placeholder="Search" />
			</div>

			<div class="li-content">
				<div class="cont-item">
					<a-directory-tree v-model:expandedKeys="expandedKeys"
					    v-model:selectedKeys="selectedKeys"
					    :multiple="true"
					    :showIcon="false"
					    :showLine="true"
					    :autoExpandParent="autoExpandParent"
					    @select="onSelect"
					    @expand="onExpand"
					    :tree-data="treeData">
						<template #title="{ title }">
							<span v-if="title.indexOf(searchValue) > -1"
							    :title="title">
								{{ title.substr(0, title.indexOf(searchValue)) }}
								<span style="color: #f50">{{ searchValue }}</span>
								{{ title.substr(title.indexOf(searchValue) + searchValue.length) }}
							</span>
							<span v-else>{{ title }}</span>
						</template>
					</a-directory-tree>

					<!-- <a-tree v-model:expandedKeys="expandedKeys"
                        Comp() {

                        },
                        :auto-expand-parent="autoExpandParent"
                        :tree-data="treeData"
                        @expand="onExpand"
                        :showLine="true"
                        :blockNode="true"
                        @select="onSelect">
                        <template #title="{ title }">
                            <span v-if="title.indexOf(searchValue) > -1">
                                {{ title.substr(0, title.indexOf(searchValue)) }}
                                <span style="color: #f50">{{ searchValue }}</span>
                                {{ title.substr(title.indexOf(searchValue) + searchValue.length) }}
                            </span>
                            <span v-else>{{ title }}</span>
                        </template>
                    </a-tree> -->
				</div>

				<div class="cont-item"></div>
			</div>

			<div class="tip-box">
				*注: 按住Ctrl可以多选, 进行事件交互
			</div>

			<div class="icon-item">
				<double-right-outlined />
			</div>
		</div>

		<!-- v-if="multipleComp.length===0" -->

		<layerInfo :layerData="layerData"
		    v-show="showLayerInfo"
		    @tabClick="initWatch"
		    ref="layerInfo"></layerInfo>
	</div>
</template>

<script>
import { commComps, prjConfig } from '@/components'
import layerInfo from './layerInfo'

let allComps = []

export default {
	des: '组件调试页面',
	name: 'compTest',
	components: { layerInfo },
	mixins: [],
	props: {},
	// setup() {},
	data() {
		return {
			treeData: [],
			allKeysTitles: [], //所有key, title

			showTip: true,
			curComp: {}, //当前组件

			layerData: {},
			showLayerInfo: true,

			searchValue: '',
			expandedKeys: [], //展开指定的树节点
			selectedKeys: [],
			autoExpandParent: true
		}
	},
	computed: {
		multipleComp() {
			const compNames = this.selectedKeys.filter(
				s => s.indexOf('isComp-') === 0
			)
			if (compNames.length <= 1) return []

			this.unWatch()
			let arr = []

			compNames.forEach(compName => {
				const name = compName.split('-').slice(-1)[0]
				const { width, height, componentName } = this.findByName(name)
				arr.push({ width, height, componentName })
			})

			return arr
		},

		contentStyle() {
			if (this.selectedKeys.length > 1)
				return 'display: inline-flex;flex-wrap:wrap'

			const { clientWidth, clientHeight } = document.body

			const vWidth = clientWidth - 10
			const vHeight = clientHeight - 62 - 10

			const curCompW = parseInt(this.curComp.width)
			const curCompH = parseInt(this.curComp.height)

			const display = curCompW > vWidth && curCompH > vHeight ? 'block' : 'flex'

			const justifyContent = curCompW < vWidth ? 'center' : 'unset'
			const alignItems = curCompH < vHeight ? 'center' : 'unset'

			return `display: ${display}; justify-content: ${justifyContent}; align-items: ${alignItems}`
		}
	},
	watch: {
		searchValue(value) {
			if (!value) this.expandedKeys = this.selectedKeys
			else {
				const expanded = this.allKeysTitles
					.map(item => {
						if (item.title.indexOf(value) > -1) {
							return this.getParentKey(item.key, this.treeData)
						}
						return null
					})
					.filter(item => !!item)
				this.expandedKeys = expanded
			}

			this.autoExpandParent = true
		}
	},
	created() {
		this.init()
	},
	mounted() {},
	activated() {},
	methods: {
		init() {
			this.initAllComps()
			this.initTreeData()
			this.initData()
		},

		initAllComps() {
			//去掉属性配置组件
			const filterAttrFn = compsArr =>
				compsArr.map(o => ({
					...o,
					children: o.children.map(v => ({
						...v,
						children: v.children.filter(
							item =>
								item.componentName.lastIndexOf('Attr') !==
								item.componentName.length - 4
						)
					}))
				}))

			//去掉属性配置组件
			const comm = filterAttrFn(commComps)
			const prj = filterAttrFn(prjConfig)

			allComps = [...comm, ...prj]
		},

		initTreeData() {
			//生成 this.treeData
			allComps.forEach(oneO => {
				this.treeData.push({
					title: oneO.label,
					key: `${oneO.label}-${oneO.value}`,
					children: oneO.children.map(twoO => ({
						title: twoO.label,
						key: `${twoO.label}-${twoO.value}`,
						children: twoO.children.map((comp, i) => ({
							title: `${i + 1}-${comp.des || ''}`,
							// title: `${i + 1}-${comp.des || ''}-${comp.componentName}`,
							key: `isComp-${i + 1}-${comp.componentName}`
						}))
					}))
				})
			})

			//生成 this.allKeysTitles
			const generateList = data => {
				for (let i = 0; i < data.length; i++) {
					const node = data[i]
					this.allKeysTitles.push({ key: node.key, title: node.title })
					if (node.children) {
						generateList(node.children)
					}
				}
			}

			generateList(this.treeData)
		},

		initData() {
			let saveInfo = JSON.parse(localStorage.getItem('savePrjComp')) || []
			const lastItem = saveInfo.slice(-1)[0] || ''
			const compName = lastItem.split('-').slice(-1)[0] //组件名
			const comp = this.findByName(compName)

			if (comp) {
				this.showTip = false
				this.selectedKeys.push(...saveInfo)
				this.expandedKeys = [lastItem] //展开
				this.autoExpandParent = true
				this.curComp = comp
			} else {
				this.showTip = true
				this.selectedKeys = []
				this.expandedKeys = []
				this.curComp = {}
			}

			this.layerData = {}
			this.changeCompName(comp)
		},

		onExpand(keys) {
			// this.expandedKeys = keys
			this.autoExpandParent = false
		},

		onSelect(keys, e) {
			const item0 = keys.slice(-1)[0]
			if (item0.indexOf('isComp') === -1) return

			const compName = item0.split('-').slice(-1)[0]
			const curComp = this.findByName(compName)
			this.changeCompName(curComp)

			localStorage.setItem('savePrjComp', JSON.stringify(keys))
		},

		//切换项目组件名称
		changeCompName(curComp) {
			if (!curComp || Object.values(curComp).length === 0) {
				this.curComp = {}
				return
			}

			if (curComp.componentName.slice(-4) === 'Attr') {
				this.curComp = curComp
				this.showLayerInfo = false
				return
			}

			const { componentName, devInfo, vm } = curComp
			const propsArr = [vm.props, ...vm.mixins.map(o => o.props)].filter(
				props => !!props
			)

			const { attrConfig } = propsArr.find(o => o.attrConfig) || {}
			const { dataConfig } = propsArr.find(o => o.dataConfig) || {}
			const { eventConfig } = propsArr.find(o => o.eventConfig) || {}

			this.layerData = {
				componentName,
				attrConfig: attrConfig && attrConfig.default(),
				dataConfig: dataConfig && dataConfig.default(),
				eventConfig: eventConfig && eventConfig.default(),
				devInfo
			}

			this.showLayerInfo = true
			if (this.multipleComp.length === 0) this.initWatch()

			this.curComp = curComp
		},

		//查找当前组件信息
		findComp() {
			if (this.selectItem.length === 0) return {}
			const oneO = allComps.find(o => o.value === this.selectItem[0])

			if (!oneO) return {}

			const twoO = oneO.children.find(o => o.value === this.selectItem[1])
			const compO = twoO.children.find(
				o => o.componentName === this.selectItem[2]
			)
			return compO || {}
		},

		//根据组件名查找组件信息
		findByName(compName) {
			if (!compName) return

			for (let i = 0; i < allComps.length; i++) {
				const oneChild = allComps[i].children
				const oneValue = allComps[i].value

				for (let n = 0; n < oneChild.length; n++) {
					const twoChild = oneChild[n].children
					const twoValue = oneChild[n].value

					const comp = twoChild.find(o => o.componentName === compName)

					if (!!comp) return comp
				}
			}
		},

		getParentKey(key, treeData) {
			let parentKey
			for (let i = 0; i < treeData.length; i++) {
				const node = treeData[i]
				if (node.children) {
					if (node.children.some(item => item.key === key)) {
						parentKey = node.key
					} else if (this.getParentKey(key, node.children)) {
						parentKey = this.getParentKey(key, node.children)
					}
				}
			}
			return parentKey
		},

		initWatch() {
			this.unWatch()

			this.$nextTick(() => {
				const layerEl = this.$refs.layerInfo
				const { myAttrConfig } = layerEl.$refs.attrComp || []
				const { myDataConfig } = layerEl.$refs.dataComp || {}
				const { myEventConfig } = layerEl.$refs.eventComp || {}

				if (myAttrConfig) {
					this.stopWatchAttr = this.$watch(
						() => this.$refs.layerInfo.$refs.attrComp.myAttrConfig,
						newAttr => {
							const comp = this.$refs.curComp
							if (comp && comp.changeAttrConfig) {
								const oldAttr = _.cloneDeep(this.layerData.attrConfig)
								this.layerData.attrConfig = _.cloneDeep(newAttr)
								this.$nextTick(() => comp.changeAttrConfig(newAttr, oldAttr))
							}
						},
						{ deep: true }
					)
				}

				if (myDataConfig) {
					this.stopWatchData = this.$watch(
						() => this.$refs.layerInfo.$refs.dataComp.myDataConfig,
						newData => {
							const comp = this.$refs.curComp
							if (comp && comp.changeDataConfig) {
								const oldData = _.cloneDeep(this.layerData.dataConfig)
								this.layerData.dataConfig = _.cloneDeep(newData)
								this.$nextTick(() => comp.changeDataConfig(newData, oldData))
							}
						},
						{ deep: true }
					)
				}

				if (myEventConfig) {
					this.stopWatchEvent = this.$watch(
						() => myEventConfig,
						newEvent => {
							const comp = this.$refs.curComp
							if (comp && comp.changeEventConfig) {
								const oldEvent = _.cloneDeep(this.layerData.eventConfig)
								this.layerData.eventConfig = _.cloneDeep(newEvent)
								this.$nextTick(() => comp.changeEventConfig(newEvent, oldEvent))
							}
						},
						{ deep: true }
					)
				}
			})
		},

		unWatch() {
			this.stopWatchAttr && this.stopWatchAttr()
			this.stopWatchData && this.stopWatchData()
			this.stopWatchEvent && this.stopWatchEvent()
			this.stopWatchAttr = null
			this.stopWatchData = null
			this.stopWatchEvent = null
		}
	},
	deactivated() {},
	beforeUnmount() {
		this.unWatch()
		this.$message.destroy()
	}
}
</script>

<style scoped lang="less">
.comp-search {
	width: 100%;
	height: 100%;
	.search-box {
		position: relative;
		padding: 15px 30px;
		background: #f4f5f5;
		text-align: center;
	}
	.content-box {
		display: flex;
		justify-content: center;
		align-items: center;
		width: 100%;
		height: calc(100% - 62px);
		// min-height: 800px;
		// max-height: 900px;
		padding: 5px;
		// overflow: auto;
		background: #222;
		.content-tip {
			width: 450px;
			white-space: nowrap;
			overflow: hidden;
			font-size: 26px;
			color: #fff;
		}

		.comp-box {
			border: 2px solid #0000ff;
			box-sizing: content-box;
			flex-shrink: 0;
			// max-width: max-content;
			// max-height: max-content;
			// max-width: fit-content;
			// max-height: fit-content;
		}
	}

	.list-box {
		position: fixed;
		left: 0;
		top: 50%;
		transform: translate(-100%, -50%);
		width: 16vw;
		background-color: #e8e8e8;
		color: #333333;
		transition: transform 0.6s 0.2s;
		z-index: 10;
		&:hover {
			transform: translate(0, -50%);
			.icon-item {
				transform-origin: 50% 50%;
				transform: translateY(-50%) rotateY(180deg);
			}
		}
		&:before {
			content: '';
			position: absolute;
			left: calc(-100% - 6px);
			top: 50%;
			transform: translate(100%, -50%);
			width: calc(100% + 6px);
			height: 99vh;
			background-color: transparent;
			z-index: -1;
			// pointer-events: none;
		}

		.li-search {
			padding: 5px 8px;
			background-color: #d3d3d3;
			border-bottom: 1px solid #aaaaaa;
			.search-item {
				border-radius: 20px;
				height: 2em;
				line-height: 2;
			}
		}

		.li-content {
			width: 100%;
			max-height: calc(100vh - 41px - 33px);
			padding: 5px 8px;
			padding-left: 0px;
			overflow-x: hidden;
			overflow-y: auto;
			.cont-item {
			}
		}

		.tip-box {
			padding: 5px 8px;
			background-color: #d3d3d3;
			border-top: 1px solid #aaaaaa;
		}

		.icon-item {
			position: absolute;
			left: 100%;
			top: 50%;
			transform: translateY(-50%);
			padding: 0 3px;
			background-color: #e8e8e8;
		}

		:deep(.ant-tree) {
			color: inherit;
			li {
				.ant-tree-node-content-wrapper {
					color: inherit;
				}
			}
		}
	}

	.layer-box {
		position: fixed;
		right: 0;
		top: 0;
		transform: translateX(100%);
		width: 300px;
		height: 100vh;
		transition: transform 0.6s 0.1s;
		&:before {
			content: '';
			position: absolute;
			left: -8px;
			top: 0;
			width: 8px;
			height: 100%;
			background-color: transparent;
			z-index: -1;
		}
		&:hover {
			transform: translateX(0);
			.icon-box {
				opacity: 1;
			}
		}

		.icon-box {
			position: absolute;
			left: -24px;
			top: 0px;
			font-size: 1.5em;
			color: #fff;
			opacity: 0;
			z-index: 9;
			transition: 0.4s opacity;
			cursor: pointer;
			&:before {
				content: '';
				position: absolute;
				left: 50%;
				top: 50%;
				transform: translate(-50%, -50%);
				width: 100%;
				height: 100%;
			}
		}

		&.fixed {
			transform: translateX(0) !important;
			.icon-box {
				opacity: 1;
			}
		}
	}

	/* .litimg {
        position: absolute;
        left: 20px;
        top: 20px;
    }

    .nav-box {
        position: absolute;
        right: 20px;
        top: 20px;
    } */
}

/* 动画 */
.content-box {
	.content-tip {
		animation-name: loading;
		animation-duration: 2s;
		animation-timing-function: steps(7);
		animation-iteration-count: infinite;
	}
	div:nth-of-type(2) {
		animation-name: fadeIn;
		animation-duration: 0.6s;
		animation-timing-function: linear;
		animation-fill-mode: forwards;
	}
}
@keyframes loading {
	0% {
		width: 392px;
		margin-right: 40px;
	}
	to {
		width: 432px;
		margin-right: 0px;
	}
}
@keyframes fadeIn {
	0% {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}
</style>

<!--修改a-cascader内部的样式-->
<style lang="less">
.comp-test-custom {
	width: 30%;
	text-align: left;
}
</style>
